<template>
  <div class="visualization-content" @click.stop="handleClick">
    <!-- 画布tab 列表 -->
    <common-tabs
      ref="tab"
      v-model="currentTabId"
      :min-hidden-width="120"
      :tabs="tabs"
      :not-empty="true"
      :allowed-edit="true"
      :tabs-after-text="'创建画板'"
      :create-options="['系统', '报告']"
      :class="settingPanelIsOpen ? 'setting-open' : 'setting-closed'"
      :lock-mode="isLock"
      @action="tabsAction"
    >
      <template #icon="slotIconValue">
        <a-icon-font
          v-if="slotIconValue.value === '报告'"
          class="tool-icon"
          type="icona-kongbaiyebeifen6"
        ></a-icon-font>
        <a-icon-font
          v-else
          class="tool-icon"
          type="icona-kongbaiyebeifen62"
        ></a-icon-font>
      </template>
    </common-tabs>
    <!-- 功能栏 -->
    <div
      class="toolbar"
      :class="settingPanelIsOpen ? 'setting-open' : 'setting-closed'"
    >
      <a-tooltip placement="top" :mouse-enter-delay="0.5">
        <template slot="title">
          <span>查看pipeline</span>
        </template>
        <div>
          <a-button type="link" style="padding: 0;" @click="togglePipeline">
            <a-icon-font
              class="tool-icon"
              :class="pipelineShow ? 'pipeline-show' : ''"
              type="iconchakanpipelinebeifen1"
            ></a-icon-font>
          </a-button>
        </div>
      </a-tooltip>
      <div
        style="background: #999; height: 14px; margin: 10px 8px; width: 1px;"
      ></div>
      <a-tooltip v-if="!publishTime" placement="top" :mouse-enter-delay="0.5">
        <template slot="title">
          <span>发布</span>
        </template>
        <div :class="{ 'guest-mode': isVisitorAndDev || isLock }">
          <!-- <a-icon-font type="icondaochu"></a-icon-font> -->
          <a-button
            class="test"
            type="link"
            style="padding: 0;"
            @click="onPublish"
          >
            <a-icon-font type="icondaochu" class="tool-icon"></a-icon-font>
          </a-button>
        </div>
      </a-tooltip>
      <a-popover
        v-else
        placement="bottomRight"
        :overlay-style="{ width: '240px' }"
      >
        <template slot="title">
          <p class="published-text">系统已发布</p>
          <span class="publish-time">最新发布时间：{{ publishTime }}</span
          ><br />
          <span class="publish-link"
            >发布链接：<a :href="publishLink" target="_blank" class="link">{{
              publishLink
            }}</a></span
          >
        </template>
        <template slot="content">
          <a-checkbox
            :checked="cancelPublishFlag"
            :disabled="isVisitorAndDev || isLock"
            @change="onCancelPublishConfirm"
          >
            取消发布
          </a-checkbox>
        </template>
        <div :class="{ 'guest-mode': isVisitorAndDev || isLock }">
          <a-button type="link" style="padding: 0;" @click="onPublish">
            <a-icon-font
              type="iconfabuwancheng1"
              class="tool-icon"
            ></a-icon-font>
          </a-button>
        </div>
      </a-popover>
      <a-tooltip v-if="!isReportMode" placement="top" :mouse-enter-delay="0.5">
        <template slot="title">
          <span>快速生成报告</span>
        </template>
        <div :class="{ 'guest-mode': isVisitor || isLock }">
          <a-button type="link" style="padding: 0;" @click="convertToReport">
            <a-icon-font
              type="iconkuaisushengchengbaogao"
              class="tool-icon"
              style="font-size: 17px; transform: translateY(1px);"
            />
          </a-button>
        </div>
      </a-tooltip>
      <a-tooltip v-if="isReportMode" placement="top" :mouse-enter-delay="0.5">
        <template slot="title">
          <span>插入空白页</span>
        </template>
        <div :class="{ 'guest-mode': isVisitor || isLock }">
          <a-button
            type="link"
            style="padding: 0;"
            @click="autoFillSplit('add')"
          >
            <a-icon-font
              type="iconcharukongbaiye"
              class="tool-icon"
              style="font-size: 17px; transform: translateY(1px);"
            />
          </a-button>
        </div>
      </a-tooltip>
      <a-tooltip placement="top" :mouse-enter-delay="0.5">
        <template slot="title">
          <span>复制</span>
        </template>
        <div :class="{ 'guest-mode': isVisitor || isLock }">
          <a-button type="link" style="padding: 0;" @click="onCopy">
            <a-icon v-if="isCopying" type="loading" />
            <a-icon-font
              v-else
              type="iconfuzhihuabu"
              class="tool-icon"
              style="font-size: 15px;"
            />
          </a-button>
        </div>
      </a-tooltip>
      <a-tooltip placement="top" :mouse-enter-delay="0.5">
        <template slot="title">
          <span>保存</span>
        </template>
        <div :class="{ 'guest-mode': isVisitor || isLock }">
          <a-button type="link" style="padding: 0;" @click="onSave(false)">
            <a-icon-font
              type="iconbaocun"
              class="tool-icon"
              style="font-size: 15px;"
            />
          </a-button>
        </div>
      </a-tooltip>
      <a-tooltip v-if="!isReportMode" placement="top" :mouse-enter-delay="0.5">
        <template slot="title">
          <span>布局</span>
        </template>
        <div :class="{ 'guest-mode': isVisitor || isLock }">
          <a-button type="link" style="padding: 0;" @click="flexLayout">
            <a-icon-font
              type="iconbuju"
              class="tool-icon"
              style="font-size: 17px; transform: translateY(1px);"
            />
          </a-button>
        </div>
      </a-tooltip>
      <a-tooltip placement="top" :mouse-enter-delay="0.5">
        <template slot="title">
          <span>预览</span>
        </template>
        <div>
          <a-button type="link" style="padding: 0;" @click="onPreview">
            <a-icon-font
              type="iconicon-beifen3"
              class="tool-icon"
              style="font-size: 17px;"
          /></a-button>
        </div>
      </a-tooltip>

      <div
        style="background: #999; height: 14px; margin: 10px 8px; width: 1px;"
      ></div>
      <a-tooltip placement="top" :mouse-enter-delay="0.5">
        <template slot="title">
          <span>redo</span>
        </template>
        <div>
          <a-button
            type="link"
            style="padding: 0;"
            @click="undoRedoFunc('redo')"
          >
            <a-icon-font
              type="iconqianjin1"
              class="tool-icon"
              style="font-size: 17px;"
          /></a-button>
        </div>
      </a-tooltip>
      <a-tooltip placement="top" :mouse-enter-delay="0.5">
        <template slot="title">
          <span>undo</span>
        </template>
        <div>
          <a-button
            type="link"
            style="padding: 0;"
            @click="undoRedoFunc('undo')"
          >
            <a-icon-font
              type="iconhoutui2"
              class="tool-icon"
              style="font-size: 17px;"
          /></a-button>
        </div>
      </a-tooltip>
    </div>
    <div ref="containerbox" class="content-box">
      <!-- 参考线开启/关闭控制 -->
      <div
        class="corner"
        :title="isShowRulerLine ? '隐藏参考线' : '显示参考线'"
        @click="setRulerStatus"
      >
        <img
          :title="isShowRulerLine ? '隐藏参考线' : '显示参考线'"
          :src="`${publicPath}/icon/ruler-line-${
            isShowRulerLine ? 'hide' : 'expand'
          }.svg`"
          alt="icon"
        />
      </div>
      <!-- 角边线 -->
      <div class="corner-border"></div>
      <!-- 标尺x -->
      <div ref="rulerx" class="ruler ruler-x">
        <div
          class="tick-box x"
          :style="{
            transform: `scaleX(${dataStore.scaleValue / 100}) translateX(${
              (-1 * scrollDistance.left) / (dataStore.scaleValue / 100)
            }px)`,
          }"
        >
          <span
            v-for="i in Math.ceil(
              (containerWidth * 2) / 100 / (dataStore.scaleValue / 100)
            )"
            :key="i"
            class="ruler-x-number"
          >
            <span
              class="ruler-number-text"
              :style="{
                transform: `scaleX(${1 / (dataStore.scaleValue / 100)})`,
              }"
            >
              {{ (i - 1) * 100 + containerOffset.x }}
            </span>
          </span>
        </div>
      </div>
      <!-- 标尺y -->
      <div
        ref="rulery"
        class="ruler ruler-y"
        :style="{ marginTop: `${publishBoxHeight}px` }"
      >
        <div
          class="tick-box y"
          :style="{
            transform: `scaleY(${dataStore.scaleValue / 100}) translateY(${
              (-1 * scrollDistance.top) / (dataStore.scaleValue / 100)
            }px)`,
          }"
        >
          <span
            v-for="i in Math.ceil(
              (containerHeight * 4 + 300) / 100 / (dataStore.scaleValue / 100)
            )"
            :key="i"
            class="ruler-y-number"
          >
            <span
              class="ruler-number-text"
              :style="{
                transform: ` scaleY(${1 / (dataStore.scaleValue / 100)})`,
              }"
            >
              {{ (i - 1) * 100 + containerOffset.y }}
            </span>
          </span>
        </div>
      </div>
      <!-- 画布盒子 -->
      <div
        class="graph-box"
        :class="{
          'is-setting-panel-open': settingPanelIsOpen,
          'is-recommand-panel-open': false,
        }"
        @scroll="onGraphBoxScroll"
      >
        <!-- 占位盒子，由于 transform 不会影响占用宽高，会导致 graph-box 内部占用的高度不变 -->
        <div
          class="real-size-block"
          :class="{
            'report-mode': isReportMode,
          }"
          :style="realSizeStyle"
          @mouseout="onContainerMouseOut"
          @mouseenter="onContainerMouseEnter"
        >
          <!-- 布局盒子 -->
          <div class="graph" :style="graphStyle">
            <!-- 切换tab 时 loading mask -->
            <div v-show="isDashboardLoading" class="layer-loading">
              <a-spin size="large" :delay="500" tip="Loading..."></a-spin>
            </div>
            <GridLayoutDesign
              ref="gridLayout"
              :hold-height="holdHeightUpdate"
              :is-lock="isLock"
              @layout-height="layoutHeightUpdate"
            ></GridLayoutDesign>
          </div>
        </div>
      </div>
      <!-- 参考线 -->
      <div
        v-show="showRulerLine && isShowRulerLine"
        class="ruler-line y"
        :style="{ top: lineOffsetY + 'px' }"
      ></div>
      <div
        v-show="showRulerLine && isShowRulerLine"
        class="ruler-line x"
        :style="{ left: lineOffsetX + 'px' }"
      ></div>
      <span
        v-show="showRulerLine && isShowRulerLine"
        class="rule-number y"
        :style="{ top: `${lineOffsetY}px` }"
        >{{
          parseInt(
            ((lineOffsetY - 29 + containerOffset.y) / dataStore.scaleValue) *
              100
          ) +
          scrollDistance.top / (dataStore.scaleValue / 100) -
          publishBoxHeight
        }}</span
      >
      <span
        v-show="showRulerLine && isShowRulerLine"
        class="rule-number x"
        :style="{ left: `${lineOffsetX}px` }"
        >{{
          parseInt(
            ((lineOffsetX - 29 + containerOffset.x) / dataStore.scaleValue) *
              100
          ) +
          scrollDistance.left / (dataStore.scaleValue / 100)
        }}</span
      >
    </div>
    <!-- 发布系统 - 配置选项 -->
    <a-modal
      v-model="showPublishConfirm"
      ok-text="确定"
      cancel-text="取消"
      class="publish-confim-box"
      :closable="false"
      :body-style="{
        width: '490px',
      }"
      :confirm-loading="confirmLoading"
      @ok.stop="handleOk"
      @cancel.stop="terminatePublish"
    >
      <div>
        <h3>
          <a-icon-font
            type="iconjinggaodanchuang"
            class="warning-icon"
          ></a-icon-font>
          确定要发布吗？
        </h3>
        <div
          :style="{
            margin: 'auto 35px',
          }"
        >
          <p>发布后可与他人分享可视化</p>
          <p
            :style="{
              marginBottom: '0px',
            }"
          >
            查看权限限制
            <span class="warning-text"
              >(注意数据公开后，存在隐私泄露的风险)</span
            >
          </p>
          <a-radio-group
            v-model="publishAuthType"
            class="auth-group"
            @change="onAuthChange"
          >
            <a-radio class="auth-select" :value="1">
              仅项目成员可见
            </a-radio>
            <a-radio class="auth-select" :value="2">
              部分人可见
            </a-radio>
            <a-select
              v-show="publishAuthType === 2"
              v-model="nameList"
              placeholder="请输入非本项目的用户名进行查询，可多选"
              class="name-selector"
              mode="multiple"
              @search="handleSearch"
              @change="handleChange"
            >
              <a-select-option
                v-for="option in userList"
                :key="option.id"
                :value="option.name"
              >
                {{ option.name }}
              </a-select-option>
            </a-select>
            <a-radio class="auth-select" :value="3">
              所有人可见
            </a-radio>
          </a-radio-group>
          <a-checkbox class="water-mark-check" @change="onShowWaterMarkChange">
            添加系统水印
          </a-checkbox>
        </div>
      </div>
    </a-modal>
    <!-- 发布成功后，链接信息展示 -->
    <a-modal v-model="publishModal" title="发布系统" :footer="null">
      <div class="copy-link-box">
        <p class="copy-link-text">{{ publishUrl }}</p>
        <div class="copy-link-btn" @click="handleCopyLink">复制链接</div>
      </div>
      <div class="text-under-copy">
        <span class="left-text">链接长期有效。</span>
        <a :href="publishUrl" target="_blank" class="right-text">打开链接</a>
      </div>
    </a-modal>
  </div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import { publicPath } from '@/api/constants'
import CommonTabs from '@/components/common/CommonTabs.vue'
import VisualizationStore from '@/store/modules/visualization'
import GridLayoutDesign from '@/components/visualization/grid-layout-design.vue'
import {
  publishDashboard,
  cancelPublish,
  copyDashboard,
  getDashboard,
  snapshot,
} from '@/api/dashboard'
import {
  GridItemDataInterface,
  gridItemFilterInterface,
} from '../vueGridLayout/interfate/grid-Item-data-inteface'
import ProjectStore, { Project } from '@/store/modules/project'
import { debounce, cloneDeep } from 'lodash'
import { queryInfoByName } from '@/api/user'
import UserStore from '@/store/modules/user'
import { queryRoleByProjectId } from '@/api/home'
import { Modal } from 'ant-design-vue'

@Component({
  components: {
    GridLayoutDesign,
    CommonTabs,
  },
})
export default class VisualizationContent extends Vue {
  // 项目 ID
  @Prop({ type: Number, default: 0 }) projectId!: number
  @Prop({ type: Boolean, default: false }) private isLock!: boolean

  private publicPath = publicPath
  public publishToken: string = '' //  发布链接token
  public publishModal: boolean = false //  发布成功链接是否显示
  public pipelineShow: boolean = false //  概览pipeline 是否显示
  private isDashboardLoading: boolean = false //  切换tab loading status

  /**
   * 真实盒子宽度
   */
  containerWidth: number = 0

  /**
   * 真实盒子高度
   */
  containerHeight: number = 0

  /**
   * 刻度线x偏移量
   */
  lineOffsetX: number = 0

  /**
   * 刻度线y偏移量
   */
  lineOffsetY: number = 0

  /**
   * 画布滚动实际距离
   */
  scrollDistance: { left: number; top: number } = { left: 0, top: 0 }

  /**
   * 鼠标hover状态
   */
  isMouseOver: boolean = true

  /**
   * 是否添加水印
   */
  showWatermark: boolean = false
  //  发布确认modal
  showPublishConfirm: boolean = false
  //  确认发布loading status
  public confirmLoading: boolean = false

  /**
   * 发布的权限类别
   * @type ‘1’- 仅自己可见 ‘2’- 部分人可见 ‘3’- 所有人可见
   */
  publishAuthType: number = 1

  // 模糊搜索 用户
  public userList: any[] = []

  //  selected id list
  private idList: Array<string> = []
  //  selected name list for clear Select Comp
  private nameList: Array<string> = []
  //  项目协作者列表【包括自己】
  private coProjectList: Array<string> = []
  //  所有画布tab信息
  private tabs: Array<any> = []
  //  取消发布标识
  private cancelPublishFlag: boolean = false

  public isCopying: boolean = false

  public publishLink: string | null = null

  get isShowRulerLine(): boolean {
    return this.dataStore.isShowRulerLine
  }

  localPublishTime: string | null = null
  get publishTime(): string | null {
    if (this.localPublishTime !== null) {
      return this.localPublishTime
    }
    if (VisualizationStore?.currentDashboard?.projectId !== this.projectId) {
      // 未加载完则不显示 避免闪现问题
      return null
    }
    const t = VisualizationStore.currentDashboard.publishTime
    if (t !== null && t !== undefined) {
      return `${t.slice(0, 4)}年${t.slice(5, 7)}月${t.slice(8, 10)}日${t.slice(
        11,
        13
      )}时${t.slice(14, 16)}分${t.slice(17, 19)}秒`
    }
    return null
  }

  get publishBoxHeight(): number {
    return this.publishTime === null ? 0 : 26
  }

  public get project(): Project | null {
    return ProjectStore.activeProject
  }

  private get currentDashboard() {
    return VisualizationStore?.currentDashboard
  }
  //  当前画布是否为 报告模式
  public get isReportMode() {
    return this.currentDashboard?.type === '报告'
  }

  //  当前画布的id
  public get currentTabId() {
    return VisualizationStore?.currentDashboard?.id ?? 0
  }
  //  所有画布数组集合
  public get sysTabs() {
    return VisualizationStore.sysTabs
  }

  private getPublishLink() {
    if (VisualizationStore?.currentDashboard?.projectId !== this.projectId) {
      // 未加载完则不显示 避免闪现问题
      this.publishLink = null
    }
    const link = VisualizationStore.currentDashboard.publishNo
    const { protocol, host } = window.location
    this.publishLink = `${protocol}//${host}/publish/${link}`
  }

  @Watch('currentDashboard', { deep: true })
  private handlePublishLink() {
    this.getPublishLink()
  }

  @Watch('sysTabs', { deep: true })
  private handleSysTabs(newValue: any) {
    if (newValue) {
      this.setTabsInfo()
    }
  }

  /**
   * undo、redo
   */
  public undoRedoFunc(operate: string) {
    setTimeout(() => {
      this.editFunction(operate)
    }, 100)
  }

  public async editFunction(operate: string) {
    const parameters = {
      projectId: this.projectId,
      snapshot: operate,
      dashboardId: this.currentTabId,
    }
    const response: any = await snapshot({ data: parameters })
    if (response.data.code === 100) {
      const reference: any = this.$refs.gridLayout as Vue
      await reference.init(this.currentTabId)

      this.$message.success(
        operate === 'undo' ? '撤销成功 !' : '恢复成功！',
        0.5
      )
    } else {
      this.$message.warning(response.data.tips)
    }
  }

  //  STORE 中的sysTab 重新赋值给tabs
  private setTabsInfo() {
    this.tabs = VisualizationStore.sysTabs?.map((tab: any) => {
      return {
        id: tab.id,
        name: tab.name,
        type: tab.type,
      }
    })
  }

  /**
   * SysTabs 组件增删改行为
   * @param actionInfo
   */
  public async tabsAction(actionInfo: any) {
    const { type, parameters } = actionInfo
    switch (type) {
      case 'add-tab': {
        const tabType = parameters.type === '系统' ? 'panel' : 'report'
        await this.confirmAdd(tabType)
        break
      }
      case 'change-tab':
        //  清空发布过的发布时间
        this.localPublishTime = null
        this.changeTabId(parameters.id)
        break
      case 'delete-tab': {
        await this.deleteTab(parameters)
        break
      }
      case 'edit-tab-name':
        await this.editTab(parameters)
        break

      default:
        break
    }
  }
  //  是否阻止grid-layout detect height update
  private holdHeightUpdate: boolean = false

  private layoutHeight: number = 0

  public layoutHeightUpdate(newValue: any) {
    this.layoutHeight = newValue
  }
  /**
   * 系统画布 转换为 报告画布
   */
  private async convertToReport() {
    /* 如果宽度大于595px 则缩放图片，使整体刚好为595px
    生成新的tab， 复制原画布的元素，对其尺寸进行缩放
    若宽度小于595px 则无需处理；
    高度的处理 842px, 累加 862
    */

    const originalHeight = this.layoutHeight
    const originalWidth = VisualizationStore.layoutData.width
    let widthScale = 1

    if (originalWidth > 595) {
      widthScale = Math.ceil(originalWidth / 595)
    }

    //  每页可放的实际高度698
    const pageN = Math.ceil(originalHeight / 698) //  842 - 72 * 2
    //  新增一个画布
    this.addDashboard('report', this.getLayoutData(widthScale), pageN)
  }

  get gridList(): Array<GridItemDataInterface> {
    return VisualizationStore.gridList
  }

  private getLayoutData(widthScale: number) {
    const layout: Array<any> = []
    const tables: Array<any> = []
    this.gridList.forEach((gridItem: GridItemDataInterface) => {
      gridItem.h = gridItem.h > 32 ? 32 : gridItem.h //  32 为a4 单页所容纳最大高度
      gridItem.w = Number(gridItem.w) / widthScale
      gridItem.x = Number(gridItem.x) / widthScale

      if (gridItem.chartType === 'table') {
        gridItem.chartOptions.sortCol = []
        tables.push({
          ...gridItem,
          chartOptions: {
            ...(gridItem.chartOptions || {}),
            value: [],
          },
        })
      } else if (gridItem.chartType === 'vistabs') {
        layout.push({
          ...gridItem,
        })
      } else if (gridItem.chartType === 'text') {
        gridItem.config = []
        layout.push({
          ...gridItem,
          chartOptions: {
            ...(gridItem.chartOptions || {}),
            value: '',
          },
        })
      } else if (
        gridItem.chartType === 'filterForm' ||
        (gridItem.chartType as string[]).includes('filterForm')
      ) {
        const filterFormItem = {
          chartType: Array.isArray(gridItem.chartType)
            ? gridItem.chartType.join('|')
            : gridItem.chartType,
          x: gridItem.x,
          y: gridItem.y,
          w: gridItem.w,
          h: gridItem.h,
          widgetType: gridItem.widgetType,
          minW: gridItem.minW,
          minH: gridItem.minH,
          i: gridItem.i,
          widgetId: gridItem.widgetId,
        }
        layout.push(filterFormItem)
      } else {
        layout.push({
          chartType: Array.isArray(gridItem.chartType)
            ? gridItem.chartType.join('|')
            : gridItem.chartType,
          x: gridItem.x,
          y: gridItem.y,
          w: gridItem.w,
          h: gridItem.h,
          widgetType: gridItem.widgetType,
          minW: gridItem.minW,
          minH: gridItem.minH,
          i: gridItem.i,
          widgetId: gridItem.widgetId,
          uniqueIDFromMyComponent: gridItem.uniqueIDFromMyComponent,
        })
      }
    })
    return {
      layout,
      tables,
    }
  }
  /**
   * 自动新增分隔符
   */
  private autoFillSplit(type: string = 'add') {
    const gridLayout = this.$refs.gridLayout as any

    if (gridLayout) {
      gridLayout.autoFillSplit(type)

      setTimeout(() => {
        //  update ruler
        this.initRulers()
        gridLayout.onSave()
      }, 100)
    }
  }

  /**
   * 创建新的画布时提醒modal
   */
  private confirmAdd(type: string) {
    const hideReminder =
      Number(localStorage.getItem('hideReportReminder')) || false

    if (type === 'panel' || hideReminder) {
      this.addDashboard(type)
    } else {
      const h = this.$createElement
      //  是否确定创建报告？？？
      this.$confirm({
        title: '确定创建报告吗？',
        // content: '创建后画板模式不可更改',
        content: h('div', {}, [
          h('p', '创建后画板模式不可更改'),
          h('a-checkbox', { class: 'report-confirm-check' }, '下次不再提示'),
        ]),
        okText: '确定',
        cancelText: '取消',
        centered: true,
        onOk: () => {
          const target = document.querySelector(
            '.report-confirm-check .ant-checkbox'
          )
          const disableConfirm = target?.classList.contains(
            'ant-checkbox-checked'
          )
          console.log('disableConfirm', disableConfirm)
          if (disableConfirm) {
            localStorage.setItem(
              'hideReportReminder',
              String(disableConfirm ? 1 : 0)
            )
          }
          this.addDashboard(type)
        },
      })
    }
  }

  /**
   * 创建 tab
   * @param type 新增画布的类型 report / panel
   * @param layoutData panel 转换为 report 所需数据
   * @param pageN page number
   */
  public async addDashboard(type: string, layoutData?: any, pageN?: number) {
    const parameters = {
      layout: JSON.stringify({
        w: type === 'panel' ? 1180 : 595,
        h: type === 'panel' ? 1600 : layoutData ? this.layoutHeight : 842,
        background: VisualizationStore.layoutData.background,
        themeSetting: {
          theme: 'light',
          panelBg:
            type === 'panel'
              ? {
                  type: 'default', //  default, pure
                  color: '#F3F3F7',
                }
              : {
                  type: 'pure',
                  color: '#FFFFFF',
                },
          componentBg: {
            color: 'rgb(255,255,255)',
            type: 'default',
          },
        },
        scale: VisualizationStore.scaleValue,
        filters: [],
        tables: layoutData?.tables ?? [],
        text: [],
        gridItems: layoutData?.layout ?? [],
        coordinations: VisualizationStore.coordinations,
      }),
      projectId: this.projectId,
      status: 0,
      type, //  dashboard type - panel / report
    }

    const response: any = await VisualizationStore.addSysTab(parameters)
    if (response.data.code === 100) {
      this.changeTabId(response.data.result.id)
      if (type === 'report') {
        setTimeout(() => {
          if (layoutData && pageN) {
            this.cycleFillSplit(pageN)
          } else {
            this.autoFillSplit('new')
          }
        }, 500)
      }
    }
  }
  /**
   * 循环增加分隔符
   * @param pageN 页数
   */
  public cycleFillSplit(pageN: number) {
    const gridLayout = this.$refs.gridLayout as any
    this.holdHeightUpdate = true
    if (gridLayout) {
      let i = 1
      while (i <= pageN) {
        gridLayout.autoFillSplit('new', i)
        i += 1
      }
      this.holdHeightUpdate = false
      setTimeout(() => {
        gridLayout.onSave()
      }, 100)
    }
  }

  /**
   * 删除 tab
   */
  public async deleteTab(info: any) {
    const { id, activeId } = info
    const parameters: any = {
      projectId: this.projectId,
      id,
    }
    const response: any = await VisualizationStore.deleteSysTab(parameters)
    if (response.data.code === 100) {
      this.changeTabId(activeId)
    } else if (response.data.code === 500) {
      this.$message.error(response.data.tips)
    }
  }

  /**
   * 修改tabName
   */
  public async editTab(info: any) {
    const { id, newName } = info
    const currentTabs: any = this.tabs.filter((tab: any) => id === tab.id)[0]
    if (newName !== currentTabs.name) {
      await this.onSave(true, () => {}, newName)
      VisualizationStore.updateTabName({
        id,
        newName,
      })
    }
  }

  /**
   * 切换 tab
   * @param id
   */
  public changeTabId(id: number) {
    if (this.currentTabId !== id) {
      this.isDashboardLoading = true
      // 防止切换tab 两个高度不一致 引起的增加分隔符的情况
      this.holdHeightUpdate = true

      const panelData = this.sysTabs.find((item: any) => item.id === id)
      VisualizationStore.setCurrentDashboardData(panelData)
      VisualizationStore.setSettingPanelIsOpen(!(panelData.type === '报告')) // 报告模式默认收起画布配置面板
      //  切换tab 滚动至新画布顶端
      const element = document.querySelector('.graph-box') as HTMLElement
      element.scrollTo(0, 0)

      //  update ruler
      this.initRulers()

      setTimeout(() => {
        this.holdHeightUpdate = false
        this.isDashboardLoading = false
      }, 1000)
    }
  }

  public get isVisitorAndDev() {
    return UserStore.isProjectGuest || UserStore.isProjectDeveloper
  }

  public get isVisitor() {
    return UserStore.isProjectGuest
  }

  public handleCopyLink() {
    navigator.clipboard.writeText(this.publishUrl).then(
      () => {
        this.$message.success('发布链接复制成功')
      },
      (error: any) => {
        this.$message.error(error)
      }
    )
  }

  public handleClick() {
    VisualizationStore.changePreviewPopStatus('close')
  }

  public toggleSettingPanel() {
    VisualizationStore.setSettingPanelIsOpen(!this.settingPanelIsOpen)
  }

  public togglePipeline() {
    this.pipelineShow = !this.pipelineShow
    this.$emit('toggle-pipeline')
  }

  public get publishUrl(): string {
    const { protocol, host } = window.location
    return `${protocol}//${host}/publish/${this.publishToken}`
  }

  private onShowWaterMarkChange(event: any) {
    this.showWatermark = event.target.checked
  }

  private onAuthChange(event: any) {
    this.publishAuthType = event.target.value
  }

  // 关键字 模糊搜索用户
  public handleSearch(searchKey: string) {
    if (searchKey.trim().length >= 2) {
      this.queryUsersByName(searchKey.trim())
    }
  }

  //  发布 - 部分人可见 - 多选处理函数
  public handleChange(value: any, options: any) {
    const result: any = []
    options.forEach((item: any) => {
      result.push(item.key)
    })
    this.idList = result
  }

  private queryUsersByName = debounce(
    (searchKey: string) => this._handleSearch(searchKey),
    100
  )
  // 模糊搜索用户
  public _handleSearch(searchKey: string) {
    const parameters = {
      name: searchKey,
    }
    queryInfoByName({ data: parameters })?.then((response: any) => {
      if (response.data.code === 100) {
        // 去掉自己
        const filterUsers = response.data.result.filter(
          (user: any) => user.id !== UserStore.currentUser?.id
        )
        this.userList = filterUsers
      }
    })
  }
  //  取消发布确认框
  public onCancelPublishConfirm() {
    const that = this
    Modal.confirm({
      title: '确定取消系统发布吗？',
      content: '取消发布后，已生成的链接将无法访问项目内容。',
      okText: '确定',
      cancelText: '取消',
      async onOk() {
        const response = await cancelPublish({
          data: {
            projectId: that.projectId,
            id: VisualizationStore.currentDashboard.id,
          },
        })
        if (response.data.result) {
          //  清空发布时间
          that.localPublishTime = null
          const index = that.sysTabs.findIndex(
            (item: any) => item.id === that.currentTabId
          )
          if (index > 0) {
            that.$set(that.sysTabs[index], 'publishTime', null)
          }
          that.$message.success('取消发布成功')
        } else {
          that.$message.error('取消发布失败')
        }
      },
    })
  }
  /**
   * 点击发布按钮
   */
  public onPublish(): void {
    if (this.isVisitorAndDev || this.isLock) {
      return
    }
    const dashboardId = VisualizationStore.currentDashboard.id
    if (!dashboardId) {
      return
    }
    // 屏蔽没有组件的情况
    if (VisualizationStore.gridList.length === 0) {
      this.$message.error('面板不存在组件')
      return
    }
    // 先保存，在发布，排除发布后空白页的问题
    const visualizationContent = VisualizationStore.VisualizationContent as any

    if (
      visualizationContent &&
      typeof visualizationContent.onSave === 'function'
    ) {
      this.showPublishConfirm = true
      //  获取协作者列表
      this.getCoProjectList()
      visualizationContent.onSave(true)
    }
  }
  /**
   * 发布系统modal - 点击取消
   */
  private terminatePublish() {
    this.showPublishConfirm = false
    this.publishAuthType = 1
    this.userList = []
    this.idList = []
    this.nameList = []
  }

  private async getRoleByProjectId() {
    const postData = {
      projectId: this.projectId,
    }
    const responseRole = await queryRoleByProjectId({ data: postData })
    return responseRole.data.result
  }
  //  获取协作者列表
  private async getCoProjectList() {
    const list = await this.getRoleByProjectId()
    const result: any = []
    list.forEach((item: any) => {
      result.push(item.userId)
    })
    this.coProjectList = result
  }
  //  获取发布系统时添加的用户id
  private getVisitorId() {
    let result: any
    const idListCopy: any = cloneDeep(this.idList)
    switch (this.publishAuthType) {
      case 1:
        result = this.coProjectList.join(',') //  need to add co-worker id
        break
      case 2:
        idListCopy.push(this.coProjectList) //  need to add co-worker id
        result = idListCopy.join(',')
        break
      case 3:
        result = '-1'
        break
      default:
        break
    }

    return result
  }
  //  确认发布
  private async handleOk() {
    const dashboardId = VisualizationStore.currentDashboard.id

    this.confirmLoading = true

    const response = await publishDashboard({
      data: {
        projectId: this.projectId,
        id: dashboardId,
        isComplex: true,
        showWatermark: this.showWatermark,
        name: VisualizationStore.currentDashboard.name,
        visitorIds: this.getVisitorId(),
      },
    })

    if (response.data.code === 100 && response.data.success) {
      this.publishToken = response.data.result
      this.terminatePublish()
      this.publishModal = true
      // this.cancelPublishFlag = false
      const d = new Date()
      this.localPublishTime = `${d.getFullYear()}年${
        d.getMonth() + 1
      }月${d.getDate()}日`

      //  设置发布时间
      const index = this.sysTabs.findIndex(
        (item: any) => item.id === this.currentTabId
      )
      if (index > 0) {
        this.$set(this.sysTabs[index], 'publishTime', new Date().toISOString())
        // 更新当前画布的publishNo
        this.$set(
          VisualizationStore.currentDashboard,
          'publishNo',
          this.publishToken
        )
      }
    } else {
      this.$message.error(response.data.tips)
    }
    this.confirmLoading = false
  }

  /**
   * 监听画布鼠标移动
   */
  onContainerMouseMove(event: any): void {
    if (event) {
      this.isMouseOver = true
      const point = this.getOffset(this.$refs.containerbox)
      this.lineOffsetX = (event.pageX as number) + point.x
      this.lineOffsetY = (event.pageY as number) + point.y
    }
  }

  /*
   * 鼠标移出
   */
  onContainerMouseOut(/* event: any */): void {
    this.isMouseOver = false
  }

  /*
   * 鼠标移入
   */
  onContainerMouseEnter(/* event: any */): void {
    this.isMouseOver = true
  }

  /**
   * 获取某个元素距离body左上角的距离
   */
  getOffset(target: any): { x: number; y: number } {
    let ele = target
    let o = ele
    let x = 0
    let y = 0
    while (o.offsetParent) {
      x += o.offsetLeft
      y += o.offsetTop
      o = o.offsetParent
    }
    // 处理当元素处于滚动之后的情况
    let left = 0
    let top = 0
    while (ele.parentNode) {
      left += ele.scrollLeft
      top += ele.scrollTop
      ele = ele.parentNode
    }
    return {
      x: left - x,
      y: top - y,
    }
  }

  /**
   * 是否展示参考线
   */
  public get showRulerLine(): boolean {
    return (
      this.isMouseOver &&
      this.lineOffsetY > 29 + this.publishBoxHeight &&
      VisualizationStore.layoutData.height + 50 > this.lineOffsetY &&
      this.lineOffsetX > 30 &&
      VisualizationStore.layoutData.width + 50 > this.lineOffsetX
    )
  }

  /**
   * 真实盒子占用宽高
   */
  get realSizeStyle() {
    return {
      width: `${
        (VisualizationStore.layoutData.width * VisualizationStore.scaleValue) /
        100
      }px`,
      minHeight: `${VisualizationStore.layoutData.height}px`,
    }
  }

  /**
   * 画布样式
   */
  get graphStyle() {
    return {
      transform: `scale(${VisualizationStore.scaleValue / 100}, ${
        VisualizationStore.scaleValue / 100
      })`,
      width: `${VisualizationStore.layoutData.width}px`,
      // minHeight: `${VisualizationStore.layoutData.height}px`,
      // height: `${VisualizationStore.layoutData.height}px`,
      background: this.getThemeBgSet(),
      // transition: 'all 0.75s',
    }
  }
  //  根据主题返回背景颜色/图片设置
  private getThemeBgSet() {
    const { theme } = VisualizationStore.layoutData.themeSetting
    const { color, type } = VisualizationStore.layoutData.themeSetting.panelBg
    if (type === 'pure' && !['blue', 'nebula'].includes(theme)) {
      return color
    }
    switch (theme) {
      case 'light':
      case 'dark':
        return color
      case 'textured':
        return `url(${publicPath}/img/vis-sys/textured-bg.jpg) top/100% repeat-y`
      case 'simple':
        return `url(${publicPath}/img/vis-sys/simple-bg.jpg) top/100% repeat-y`
      case 'nebula':
        return `url(${publicPath}/img/vis-sys/nebula-bg.jpg) top/100% repeat-y`
      case 'blue':
        return `url(${publicPath}/img/vis-sys/blue-bg.jpg) top/100% repeat-y`
      default:
        return '#FFF'
    }
  }

  /**
   * store 模板使用变量
   */
  get dataStore() {
    return VisualizationStore
  }

  /**
   * 同步设置弹框展示状态
   */
  get settingPanelIsOpen() {
    return this.isVisitor || this.isLock
      ? false
      : VisualizationStore.settingPanelIsOpen
  }

  /**
   * 右侧setting panel 展开收起后，根据新的宽度重新计算每个tab宽度
   */
  @Watch('settingPanelIsOpen')
  public handlePanelIsOpen() {
    //  因panel有一个transition, 故重新计算宽度要在其之后
    setTimeout(() => {
      const commonTabs = this.$refs.tab as any
      commonTabs.calcTabWidth()
    }, 300)
  }

  /**
   * 激活选项
   */
  get activeSettingItem() {
    return VisualizationStore.activeSettingItem
  }

  @Watch('activeSettingItem')
  handleActiveSetItem(newValue: any, oldValue: any) {
    if (oldValue === 'chart' && newValue) {
      this.onSave()
    }
  }

  /**
   * 画布偏移量
   */
  public containerOffset: { x: number; y: number } = { x: 0, y: 0 }

  mounted() {
    const win = window as any
    const that = this
    this.initRulers()
    function resize() {
      that.initRulers()
    }
    win.addEventListener('resize', resize)
    this.$once('hook:beforeDestroy', () => {
      win.removeEventListener('resize', resize)
    })
    // 每次进来组件面板恢复原样
    VisualizationStore.setIsFullMode(false)
    VisualizationStore.setVisualizationContent(this)
    this.dataStore.eventBus.$on('add-panel-dashboard', this.addDashboard)
  }

  beforeDestroy() {
    this.destroyEventBus()
  }

  destroyEventBus() {
    this.dataStore.eventBus.$off('add-panel-dashboard')
  }

  /**
   * 初始化标尺
   */
  initRulers() {
    const rulerx = this.$refs.rulerx as HTMLElement
    const rulery = this.$refs.rulery as HTMLElement
    if (rulerx) {
      this.containerWidth = rulerx.offsetWidth
    }
    if (rulery) {
      this.containerHeight = Math.max(
        rulery.offsetHeight,
        this.dataStore.layoutData.height
      )
    }
  }

  /**
   * 设置参考线状态
   */
  setRulerStatus() {
    this.dataStore.setIsShowRulerLine(!this.isShowRulerLine)
  }

  /**
   * 画布盒子滚动监听
   */
  onGraphBoxScroll(event: Event) {
    const element = event.target as HTMLElement
    if (element) {
      this.scrollDistance = {
        left: element.scrollLeft,
        top: element.scrollTop,
      }
    }
  }

  /**
   * 同步控件组件的高度
   */
  syncFilterHeight(gridItem: gridItemFilterInterface, callback?: Function) {
    const gridLayout = this.$refs.gridLayout as any
    if (gridLayout) {
      gridLayout.syncFilterHeight(gridItem, callback)
    }
  }

  /**
   * 编辑/添加控件（此处用于main.vue调用）
   */
  editChart(gridItem: GridItemDataInterface) {
    const gridLayout = this.$refs.gridLayout as any
    if (gridLayout) {
      gridLayout.editChart(gridItem)
    }
  }

  /**
   * 保存
   */
  async onSave(noMessage = true, callback?: Function, name?: string) {
    if (this.isVisitor || this.isLock) {
      return
    }
    const gridLayout = this.$refs.gridLayout as any
    if (gridLayout) {
      await gridLayout.onSave(noMessage, callback, name)
    }
  }

  /**
   * 预览
   */
  onPreview() {
    VisualizationStore.changeVisualizationMode('preview')
  }

  get visMode() {
    return VisualizationStore.visualizationMode
  }

  //  切换编辑态 - 预览态的时候，重新复制tabs，解决tab 为空的问题
  @Watch('visMode')
  handleVisModeChange(newValue: string) {
    if (newValue === 'edit') {
      this.setTabsInfo()
    } else {
      this.tabs = []
    }
  }

  /**
   * 布局
   */
  flexLayout() {
    if (this.isVisitor || this.isLock) {
      return
    }
    const gridLayout = this.$refs.gridLayout as any
    if (gridLayout) {
      gridLayout.flexLayout()
    }
  }

  /**
   * 画布复制
   */
  async onCopy() {
    if (this.isVisitor || this.isLock) {
      return
    }
    this.isCopying = true
    const { data } = await copyDashboard({
      data: {
        projectId: this.projectId,
        dashboardId: this.currentTabId,
      },
    })
    const { result, code } = data
    if (code === 100) {
      // 获取新的dashboardId，并根据dashboardId发起查询
      const { data: tabData } = await getDashboard({
        data: {
          id: result,
        },
      })
      // 新增画布tab信息
      VisualizationStore.setSysTabs([...this.sysTabs, tabData?.result])
      VisualizationStore.setCurrentDashboardData(tabData?.result)
    } else {
      this.$message.error('复制失败')
    }
    this.isCopying = false
  }
}
</script>
<style lang="less" scoped>
@import '@/constants';

.visualization-content {
  background: #f8f8f8;
  display: flex;
  flex-flow: row;
  flex-flow: column;
  flex-wrap: nowrap;
  height: 100%;
  justify-content: flex-start;
  position: relative;
  width: 100%;
  z-index: 61;

  /deep/ .vue-tabs-chrome {
    width: calc(100% - 300px);

    &.setting-closed {
      transition: all 0.2s ease;
      width: 100%;
    }

    &.setting-open {
      transition: all 0.2s ease;
      width: calc(100% - 300px);
    }
  }

  .toolbar {
    background-color: #fff;
    border-bottom: 1px solid @VISUALIZATION_TOOLBAR_BORDER;
    display: flex;
    flex-direction: row-reverse;
    height: @VISUALIZATION_TOOLBAR_HEIGHT;
    line-height: @VISUALIZATION_TOOLBAR_HEIGHT;
    padding-right: 4px;
    vertical-align: middle;
    width: calc(100% - 300px);

    &.setting-closed {
      padding-right: 10px;
      width: 100%;
    }

    .left-option {
      left: 12px;
      position: absolute;
    }

    .tool-icon {
      cursor: pointer;
      font-size: 16px;
      height: 26px;
      margin: 0 2px;
      padding: 4px;

      &:hover {
        background-color: #f1f1f1;
        color: #6973ff;
      }

      &.icon-layout {
        font-size: 20px;
        // width: 18px;
      }
    }

    .pipeline-show {
      background-color: rgba(105, 115, 255, 0.1);
      color: #5561ff !important;

      &:hover {
        background-color: transparent;
      }
    }

    /deep/ .ant-btn.ant-btn-link {
      color: #5d637e;
      font-size: 14px;
      font-weight: 400;
      letter-spacing: 1px;
      line-height: 20px;

      &:hover .tool-icon {
        background-color: rgba(105, 115, 255, 0.1);
        color: #5561ff !important;
      }
    }
  }

  .content-box {
    box-sizing: border-box;
    flex: 1;
    height: 100%;
    overflow: hidden;
    padding-left: 20px;
    padding-top: 20px;
    position: relative;
    width: 100%;

    .corner-border {
      border-left: 1px solid #cacbd2;
      border-top: 1px solid #cacbd2;
      height: 38px;
      left: 20px;
      pointer-events: none;
      position: absolute;
      top: 19px;
      width: 12px;
      z-index: 220;
    }

    .ruler {
      left: 0;
      overflow: hidden;
      position: absolute;
      top: 0;
      z-index: 20;

      &.ruler-x {
        height: 20px;
        width: 100%;
      }

      &.ruler-y {
        height: calc(100vh - 100px);
        width: 21px;
      }

      .tick-box {
        left: 0;
        position: absolute;
        top: 0;

        &.x {
          background: url('~@/assets/view/ruler-x.png') repeat-x;
          background-color: #f9f9fc;
          height: 20px;
          margin-left: 30px;
          overflow: hidden;
          transform-origin: 0 0;
          width: 600%;
        }

        &.y {
          background: url('~@/assets/view/ruler-y.png') repeat-y;
          background-color: #f9f9fc;
          border-right: 1px solid #ccc;
          box-sizing: border-box;
          height: 600%;
          left: 0;
          margin-left: 1px;
          position: absolute;
          top: 29px;
          transform-origin: 0 0;
          width: 20px;
          z-index: 1000;
        }
      }
    }

    .corner {
      background: rgb(249, 249, 252);
      border: 2px solid #f8f8f8;
      height: 21px;
      left: 0;
      position: absolute;
      top: 0;
      width: 21px;
      z-index: 100;

      img {
        display: block;
        height: auto;
        transform: translate(2px, 2px);
        width: 16px;
      }
    }

    .ruler-x-number {
      box-sizing: border-box;
      color: #7a7f96;
      display: inline-block;
      font-size: 10px;
      height: 20px;
      padding-left: 2px;
      text-align: left;
      width: 100px;
    }

    .ruler-number-text {
      display: block;
      transform-origin: 0 0;
    }

    .ruler-y-number {
      box-sizing: border-box;
      color: #7a7f96;
      display: block;
      font-size: 10px;
      height: 100px;
      padding-bottom: 2px;
      padding-top: 2px;
      position: relative;
      text-align: right;
      width: 20px;

      .ruler-number-text {
        pointer-events: none;
        position: absolute;
        right: 2px;
        top: 2px;
        writing-mode: vertical-rl;
      }
    }

    .ruler-line.y {
      border: 1px dashed #aeb2f5;
      box-sizing: border-box;
      display: block;
      height: 1px;
      left: 0;
      pointer-events: none;
      position: absolute;
      top: 100px;
      width: 100%;
      z-index: 11;
    }

    .ruler-line.x {
      border: 1px dashed #aeb2f5;
      box-sizing: border-box;
      content: ' ';
      display: block;
      height: 100%;
      left: 100px;
      pointer-events: none;
      position: absolute;
      top: -10px;
      width: 1px;
      z-index: 11;
    }

    .rule-number {
      color: #5760e6;
      font-size: 10px;
      position: absolute;
      z-index: 12;

      &.x {
        display: block;
        top: 30px;
        transform: rotate(90deg) translate(4px, -2px);
      }

      &.y {
        display: block;
        left: 30px;
      }
    }
  }

  .graph-box {
    height: calc(100vh - 170px);
    overflow-y: scroll;
    padding: 6px 20px 20px 10px;
    transition: width ease-in 0.2s, height ease-in 0.2s;

    &::-webkit-scrollbar {
      height: 6px;
      visibility: hidden;
      width: 6px;
    }

    &::-webkit-scrollbar-thumb {
      background: rgba(144, 147, 153, 0.5);
      border-radius: 6px;
      visibility: hidden;
    }

    &:hover {
      &::-webkit-scrollbar {
        visibility: visible;
      }

      &::-webkit-scrollbar-thumb {
        visibility: visible;
      }
    }

    &::-webkit-scrollbar-track {
      background: transparent;
      border-radius: 5px;
    }

    &.is-setting-panel-open {
      width: calc(100% - 282px);
    }

    &.is-recommand-panel-open {
      height: calc(100vh - 330px);
    }
  }

  .real-size-block {
    box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.15);
    display: block;
    height: auto;
    overflow: hidden;
    width: 100%;
  }

  .real-size-block.report-mode {
    margin: 0 auto;
    min-height: auto !important; // 报告模式直接根据内部高度，无需外层设置minheight
  }

  .graph {
    height: auto;
    position: relative;
    transform-origin: 0 0;

    .layer-loading {
      background-color: #f0f3ff;
      display: flex;
      height: 100%;
      justify-content: center;
      left: 0;
      padding-top: 300px;
      position: absolute;
      top: 0;
      width: 100%;
      z-index: 100;
    }
  }
}

/deep/ .ant-modal-body {
  padding-bottom: 58px;
}

.copy-link-box {
  display: flex;

  .copy-link-text {
    background: #f9f9fc;
    border: 1px solid #e8e8e8;
    border-radius: 2px 0 0 2px;
    color: #5d637e;
    flex: 1;
    font-size: 14px;
    height: 32px;
    line-height: 20px;
    margin-bottom: 4px;
    overflow: hidden;
    padding: 6px 9.5px;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .copy-link-btn {
    align-items: center;
    background: #6973ff;
    border-radius: 0 4px 4px 0;
    color: #fff;
    cursor: pointer;
    display: flex;
    font-weight: 300;
    height: 32px;
    justify-content: center;
    width: 88px;
  }
}

.text-under-copy {
  display: flex;
  justify-content: space-between;

  .left-text {
    color: #222432;
    font-size: 12px;
    height: 17px;
    opacity: 0.5;
  }

  .right-text {
    color: #6973ff;
    font-size: 12px;
    height: 17px;
  }
}

.auth-group {
  margin-bottom: 25px;
  padding-left: 20px;
  width: 80%;

  .auth-select {
    display: block;
    height: 30px;
    line-height: 30px;
  }

  .name-selector {
    margin: 10px auto;
    width: 100%;
  }
}

.publish-confim-box /deep/ .ant-modal-body {
  padding: 32px 32px 24px;
}

.warning-icon {
  font-size: 20px;
  margin-right: 10px;
}

.warning-text {
  color: #e6505a;
}

.guest-mode {
  cursor: not-allowed;
  opacity: 0.45;
  user-select: none;

  /deep/ .ant-btn {
    cursor: not-allowed;
  }
}

.published-text {
  color: #373b52;
  font-size: 14px;
  font-weight: 500;
  line-height: 22px;
  margin-bottom: 5px;
}

.publish-time {
  color: #222432;
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 1.12px;
  opacity: 0.5;
}

.publish-link {
  color: #222432;
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 1.12px;
  opacity: 0.5;

  .link {
    color: #6272eb;
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}
</style>
